using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Management;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
using Client.CCS;
using Client.Properties;
//used to access the registry
    //used in isolated storage
//used for isolated Storage
    //used to write and read the xml document.
    //used to actually write the xml
    //used to get the Trial key from the 
    //used to create the unique machine ID.

namespace Client.UIHelper
{
    /// <summary>
    /// The DataStore class is used for storing and retrieving data
    /// between each time the application runs.
    /// It includes built in functions that are commonly needed by
    /// shareware application developers.
    ///	
    ///	</summary>
    ///	<remarks>	
    ///The DataStore class works by using 
    ///<a href='http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconIsolatedStorage.asp'>
    ///isolated Storage</a> found in the .Net Framework.
    ///
    ///Each time the datastore is created it attempts to find the .dat file within Isolated storage.  If it cannot be found 
    ///the web service is contacted to pull down current product state and configuration.  This way, if the isolatedStorage
    ///location changes the state is still preserved at the server within the database.  
    public class DataStore : IDisposable
    {
        /// <summary>The name of the file written to isolated storage</summary>						
        protected const string DATA_STORE_NAME = "DataStore.dat";

        #region private variables

        //initialize the logger

        private readonly Form formRef;

        /// <summary>
        /// The private instance of Islated storage to use
        /// </summary>
        private IsolatedStorageFileStream isfs;

        private Logger logger = new Logger(Application.StartupPath, "CCSClientLog.txt");

        /// <summary>
        /// If the web service request for the datastore fails we will set this flag to true
        /// and return the appropriate product state and licensing information.
        /// </summary>
        private bool m_fRequestForDataStoreFailed;

        /// <summary>
        /// The one and only storage object. This is where all of the
        /// information is cached after reading the isolated storage file. 
        /// this object	is also the  one that is serialized when and written
        ///  to the isolated storage file.
        ///  <seealso cref='WriteDataToIsolatedStorage'/>
        /// </summary>
        //private Storage storage;
        private ClientAppConfig m_storage;

        //a reference to the form that created this class

        //an event handler to signal a failure to the caller

        #endregion

        /// <summary>
        /// Default DataStore Constructor. It creates a new DataStore in isolated
        ///  storage if one is not found.
        /// </summary>
        /// <seealso cref='DefaultDataStoreValues'/>
        /// <exception cref="DataStoreWriteException(string)"/>
        /// <exception cref="DataStoreAppConfigException(string)"/>
        /// <exception cref="DataStoreUniqueMachineIDException(string)"/>
        public DataStore(Form form)
        {
            Logger.Log("DataStore : Constructor : Beginning");
            formRef = form;
            m_storage = new ClientAppConfig();
            Logger.Log("DataStore : Constructor : ReadDataFromIsolatedStorage : Beginning");
            ReadDataFromIsolatedStorage();
            Logger.Log("DataStore : Constructor : ReadDataFromIsolatedStorage : Completed");
            Logger.Log("DataStore : Constructor : Completed");
        }

        /// <summary>
        /// Handles Events that are used by the UI layer to signal a failed
        /// completion for the Serverprocessing class.  The failure message
        /// is passed pack in the event args.
        /// </summary>
        public EventHandler<ProcessingFailedEvent> DataStoreFailed { get; set; }


        /// <summary>
        /// Returns the available license levels for this product.
        /// </summary>
        public LicenseLevelsPrice[] AvailableLicenseLevels
        {
            get { return m_storage.LicenseLevelPrices; }
        }

        /// <summary>
        /// The length in ms to wait for a web service request to time out.
        /// </summary>
        public double AbortTimerLength
        {
            get { return m_storage.AbortTimerLength; }
        }

        /// <summary>
        /// The length in ms to wait before attempting to contact the web service again.
        /// </summary>
        public double PollingTimerLength
        {
            get { return m_storage.PollingTimerLength; }
        }

        /// <summary>
        /// The maximum number of times to contact the webservice before failing.
        /// </summary>
        public int BuyNowMaxNumPoll
        {
            get { return m_storage.BuyNowMaxNumPoll; }
        }

        /// <summary>
        /// The payment setting used for PayPal configuration
        /// </summary>
        public PaymentSettings PaymentSettings
        {
            get { return m_storage.PaymentSettings; }
        }

        /// <summary>
        /// The maximum number of times to contact the server when the user is
        /// entering an activation key.
        /// </summary>
        public int ActivateNowMaxNumPoll
        {
            get { return m_storage.ActivateNowMaxNumPoll; }
        }

        /// <summary>
        /// The token that represents the developer when communicating with the
        /// web service
        /// </summary>
        public string Devtoken
        {
            get { return m_storage.DevToken; }
        }

        /// <summary>
        /// The name of this product used in communication with the seb service.
        /// </summary>
        public string ProductName
        {
            get { return m_storage.ProductName; }
        }

        /// <summary>
        /// The unique identifier for this machine
        /// </summary>
        public string UniqueMachineID
        {
            get { return CreateUniqueMachineID(); }
        }

        /// <summary>
        /// Sets a flag that causes the client to contact the server for the latest data store the next
        /// time the datastore is accessed.
        /// </summary>
        public bool RefreshDataStore
        {
            set { m_storage.DataStoreSettings.RefreshDataStore = true; }

            get { return m_storage.DataStoreSettings.RefreshDataStore; }
        }

        /// <summary>
        /// Returns the license level that the product is registered for.
        /// </summary>
        /// <returns>The licenseLevelsPrice object representing the appropriate license level</returns>
        public LicenseLevelsPrice GetLicenseLevel
        {
            get
            {
                Logger.Log("DataStore : GetLicenseLevel : Beginning");

                CCS.DataStore dataStore;
                var licenseLevel = new LicenseLevelsPrice();
                licenseLevel.LicenseLevelID = Settings.Default.LicenseLevel_1_ID;
                licenseLevel.LicenseLevel = "";

                if (m_storage.DataStoreSettings != null)
                {
                    dataStore = m_storage.DataStoreSettings;
                }
                else
                {
                    Logger.Log("DataStore : GetLicenseLevel : DataStoreSettings was null");
                    return licenseLevel;
                }

                foreach (LicenseLevelsPrice licenseLevelsPrice in m_storage.LicenseLevelPrices)
                {
                    if (VerifyLicense(dataStore.RegistrationKey, licenseLevelsPrice.LicenseLevel, dataStore.UniqueMachineID))
                    {
                        Logger.Log("DataStore : GetLicenseLevel : Completed : LicenseLevel = " + licenseLevelsPrice.LicenseLevel);
                        return licenseLevelsPrice;
                    }
                }

                licenseLevel.LicenseLevel = "License Error";
                Logger.Log("DataStore : GetLicenseLevel : Error : LicenseLevel = " + licenseLevel.LicenseLevel);
                return licenseLevel;
            }
        }

        /// <summary>
        /// Returns the enumerated state of the product depending on the activation and trial status.
        /// Possibilities are Trial, ExpiredTrial, Activated, NoDataStore, and Error.
        /// </summary>
        public ProductState GetProductState
        {
            get
            {
                Logger.Log("DataStore : GetProductState : Beginning");
                var trialLicenseLevel = new LicenseLevelsPrice();

                if (AvailableLicenseLevels != null)
                {
                    foreach (LicenseLevelsPrice availableLicenseLevel in AvailableLicenseLevels)
                    {
                        if (availableLicenseLevel.IsTrialLicensekey)
                        {
                            trialLicenseLevel = availableLicenseLevel;
                            break;
                        }
                    }

                    if (GetLicenseLevel.LicenseLevel == trialLicenseLevel.LicenseLevel)
                    {
                        if (DaysElapsedSinceInstall > m_storage.TrialLengthDays)
                        {
                            Logger.Log("DataStore : GetProductState : ProductState.ExpiredTrial");
                            return ProductState.ExpiredTrial;
                        }
                        else
                        {
                            Logger.Log("DataStore : GetProductState : ProductState.Trial");
                            return ProductState.Trial;
                        }
                    }
                    else if (GetLicenseLevel.LicenseLevel == "License Error")
                    {
                        Logger.Log("DataStore : GetProductState : ProductState.Error");
                        return ProductState.Error;
                    }
                    else
                    {
                        Logger.Log("DataStore : GetProductState : ProductState.Activated");
                        return ProductState.Activated;
                    }
                }
                else
                {
                    if (m_fRequestForDataStoreFailed)
                    {
                        Logger.Log("DataStore : GetProductState : ProductState.NoDataStore");
                        return ProductState.NoDataStore;
                    }
                    else
                    {
                        Logger.Log("DataStore : GetProductState : ProductState.Error");
                        return ProductState.Error;
                    }
                }
            }
        }

        /// <summary>
        /// Gets the number of the  days that have elapsed since the product was installed.
        /// </summary>
        /// <returns>The number of days elapsed since install.</returns>
        /// <exception cref="DataStoreWriteException(string)"/>
        /// <exception cref="DataStoreReadException(string)"/>
        /// <exception cref="DataStoreChronologyException(string)">
        /// If it appears that the date time has been set back this error will be thrown.
        /// </exception>
        public int DaysElapsedSinceInstall
        {
            get
            {
                Logger.Log("DataStore : DaysElapsedSinceInstall : Beginning");
                //ReadDataFromIsolatedStorage();
                CCS.DataStore dataStoreSettings = m_storage.DataStoreSettings;

                if (dataStoreSettings != null)
                {
                    TimeSpan difference; //difference between dataStoreStartDate and currentDate.									


                    //first check to see if the system time was set back.
                    if (DateTime.Now < dataStoreSettings.StartDate)
                    {
                        Logger.Log("DataStore : DaysElapsedSinceInstall : Error : DataStoreChronologyException");
                        throw new DataStoreChronologyException("The current system time" + "is before the last known time stored in the isolated storage.");
                    }


                    difference = DateTime.Now.Subtract(dataStoreSettings.StartDate);

                    if (difference.Days < 0)
                    {
                        Logger.Log("DataStore : DaysElapsedSinceInstall : Error : DataStoreChronologyException");
                        //somehow the dataStore start date is set in the future...
                        throw new DataStoreChronologyException("The DataStore Start Date is set in the future. " + "Either the System Time was set back, or even " + "more weirder the date stored in the data store" + "was set in the future.");
                    }
                    Logger.Log("DataStore : DaysElapsedSinceInstall : Completed : difference.Day =" + difference.Days);
                    return difference.Days;
                }
                    //there is no datastore so return a null date;
                else
                {
                    return -1;
                }
            }
        }

        /// <summary>
        /// Returns a boolean value that determines if the product has been registered or not
        /// </summary>
        /// <returns>Boolean representing if product has been registered</returns>
        public bool UserRegistered
        {
            get
            {
                Logger.Log("DataStore : UserRegistered : Get : Beginning");
                //ReadDataFromIsolatedStorage();
                CCS.DataStore dataStoreSettings = m_storage.DataStoreSettings;
                if (dataStoreSettings != null)
                {
                    Logger.Log("DataStore : UserRegistered : Get : Completed : UserRegistered = " + dataStoreSettings.UserInfoRegistered);
                    return dataStoreSettings.UserInfoRegistered;
                }
                else
                {
                    Logger.Log("DataStore : UserRegistered : Get : datastore setting null : false");
                    return false;
                }
            }
            set
            {
                Logger.Log("DataStore : UserRegistered : Set : Beginning");
                CCS.DataStore dataStoreSettings = m_storage.DataStoreSettings;
                dataStoreSettings.UserInfoRegistered = value;
                m_storage.DataStoreSettings = dataStoreSettings;
                WriteDataToIsolatedStorage();
                Logger.Log("DataStore : UserRegistered : Set : Completed");
            }
        }

        #region Private Functions and Protected members

        /// <summary>
        /// Verifies the registration key against the machineID and licenseLevel included in the 
        /// RegistrationKey
        /// </summary>
        /// <param name="registrationKey">The registration key from the server to validate</param>
        /// <param name="licenseLevel">The license level to validate against</param>
        /// <param name="uniqueMachineID">The machine ID to validate against</param>
        /// <returns>True is a valid license, false is not valid for the given machine and license level</returns>
        private Boolean VerifyLicense(string registrationKey, string licenseLevel, string uniqueMachineID)
        {
            //if the license key has defaulted to the trial key return true
            foreach (LicenseLevelsPrice licenseLevelPrice in m_storage.LicenseLevelPrices)
            {
                if (licenseLevelPrice.IsTrialLicensekey && licenseLevelPrice.LicenseLevel == registrationKey)
                {
                    return true;
                }
            }

            RSACryptoServiceProvider rsa;

            byte[] authenticatedLicense;

            //validate the server public key to correct for possible typos
            if (!m_storage.ServerPublicKey.StartsWith("<RSAKeyValue><Modulus>") && !m_storage.ServerPublicKey.EndsWith("</Exponent></RSAKeyValue>"))
            {
                m_storage.ServerPublicKey = "<RSAKeyValue><Modulus>" + m_storage.ServerPublicKey;
                m_storage.ServerPublicKey += "</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
            }

            rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(m_storage.ServerPublicKey);


            authenticatedLicense = Convert.FromBase64String(registrationKey);

            if (rsa.VerifyHash(Convert.FromBase64String(ClientInfo(licenseLevel, Settings.Default.ProductName, uniqueMachineID)), CryptoConfig.MapNameToOID("SHA1"), authenticatedLicense))
            {
                if (licenseLevel == m_storage.DataStoreSettings.LicenseLevel)
                {
                    rsa.Clear();
                    return true;
                }
                else
                {
                    rsa.Clear();
                    return false;
                }
            }
            else
            {
                rsa.Clear();
                return false;
            }
        }

        /// <summary>
        /// Reads all of the data in from the isolated storage
        /// and stores it in the <see cref="storage"/> object.  If the isolated
        /// storage file is not found it attempts to contact the web service to
        /// initiate the data store.
        /// </summary>
        /// <exception cref="DataStoreAppConfigException(string)"/>
        /// <exception cref="DataStoreUniqueMachineIDException(string)"/>
        /// <exception cref="DataStoreWriteException(string)"/>
        /// <exception cref="DataStoreReadException(string)"/>
        protected void ReadDataFromIsolatedStorage()
        {
            //load the stored data into the inner class so we have easy access to it.			
            //IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForDomain();

            IsolatedStorageFile isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, null, null);

            bool fileExists = false;

            string[] fileNames = isoStore.GetFileNames(DATA_STORE_NAME);
            foreach (string file in fileNames)
            {
                if (file == DATA_STORE_NAME)
                {
                    fileExists = true;
                }
            }


            if (!fileExists)
            {
                Logger.Log("DataStore : ReadDataFromIsolatedStorage : No DataStore- Contacting Server");

                var dataStoreInfo = new DataStoreInfo(Settings.Default.DevToken, Settings.Default.ProductName, CreateUniqueMachineID(), DateTime.Now, formRef);

                ContactServerForDefaultStorage(dataStoreInfo);
                WriteDataToIsolatedStorage();
            }
            else
            {
                try
                {
                    read();

                    if (m_storage.DataStoreSettings.RefreshDataStore)
                    {
                        var dataStoreInfo = new DataStoreInfo(Settings.Default.DevToken, Settings.Default.ProductName, CreateUniqueMachineID(), DateTime.Now, formRef);
                        ContactServerToRefreshStorage(dataStoreInfo);
                        WriteDataToIsolatedStorage();
                    }
                }
                catch (Exception ex)
                {
                    Logger.Log("DataStore : ReadDataFromIsolatedStorage : Error reading store : " + ex.Message);
                    var dataStoreInfo = new DataStoreInfo(Settings.Default.DevToken, Settings.Default.ProductName, CreateUniqueMachineID(), DateTime.Now, formRef);
                    ContactServerForDefaultStorage(dataStoreInfo);
                    WriteDataToIsolatedStorage();
                }
            }
        }

        private void ContactServerForDefaultStorage(DataStoreInfo dataStoreInfo)
        {
            try
            {
                var sp = new Serverprocessing(dataStoreInfo);
                m_storage = sp.ContactServerForDefaultStorage(dataStoreInfo);
                Logger.Log("DataStore : ReadDataFromIsolatedStorage : Data Store Successfully Retrieved from server");
            }
            catch (Exception ex)
            {
                Logger.Log("DataStore : ReadDataFromIsolatedStorage : Error retrieving Data Store from server : " + ex.Message);
            }

            if (m_storage.DataStoreSettings == null)
            {
                var defaultConfig = new ClientAppConfig();
                defaultConfig.AbortTimerLength = Settings.Default.AbortTimerLength;
                defaultConfig.ActivateNowMaxNumPoll = 1;
                defaultConfig.BuyNowMaxNumPoll = Settings.Default.MaxPollNum;

                #region set default data store

                var defaultDataStore = new CCS.DataStore();
                defaultDataStore.DateTime = DateTime.Now;

                #region set default license levels

                var licenseLevel1 = new LicenseLevelsPrice();
                licenseLevel1.IsTrialLicensekey = Settings.Default.LicenseLevelIsTrialKey_1;
                licenseLevel1.LicenseLevel = Settings.Default.LicenseLevel_1;
                licenseLevel1.LicenseLevelPrice = Settings.Default.LicenseLevelPrice_1;
                licenseLevel1.LicenseLevelID = Settings.Default.LicenseLevel_1_ID;
                licenseLevel1.Description = Settings.Default.LicenseLevelDescription_1;

                var licenseLevel2 = new LicenseLevelsPrice();
                licenseLevel2.IsTrialLicensekey = Settings.Default.LicenseLevelIsTrialKey_2;
                licenseLevel2.LicenseLevel = Settings.Default.LicenseLevel_2;
                licenseLevel2.LicenseLevelPrice = Settings.Default.LicenseLevelPrice_2;
                licenseLevel2.LicenseLevelID = Settings.Default.LicenseLevel_2_ID;
                licenseLevel2.Description = Settings.Default.LicenseLevelDescription_2;

                var licenseLevel3 = new LicenseLevelsPrice();
                licenseLevel3.IsTrialLicensekey = Settings.Default.LicenseLevelIsTrialKey_3;
                licenseLevel3.LicenseLevel = Settings.Default.LicenseLevel_3;
                licenseLevel3.LicenseLevelPrice = Settings.Default.LicenseLevelPrice_3;
                licenseLevel3.LicenseLevelID = Settings.Default.LicenseLevel_3_ID;
                licenseLevel3.Description = Settings.Default.LicenseLevelDescription_3;

                defaultConfig.LicenseLevelPrices = new LicenseLevelsPrice[3] {licenseLevel1, licenseLevel2, licenseLevel3};

                defaultDataStore.LicenseLevel = licenseLevel1.LicenseLevel;
                foreach (LicenseLevelsPrice licenseLevelPrice in defaultConfig.LicenseLevelPrices)
                {
                    if (licenseLevelPrice.IsTrialLicensekey)
                    {
                        defaultDataStore.LicenseLevel = licenseLevelPrice.LicenseLevel;
                        defaultDataStore.RegistrationKey = licenseLevelPrice.LicenseLevel;
                    }
                }

                #endregion

                defaultDataStore.NumberOfTrialDaysElapsed = 0;

                defaultDataStore.StartDate = DateTime.Now;
                defaultDataStore.UniqueMachineID = UniqueMachineID;
                defaultDataStore.UserInfoRegistered = false;
                defaultDataStore.RefreshDataStore = true;

                #endregion

                defaultConfig.DataStoreSettings = defaultDataStore;
                defaultConfig.DevToken = Settings.Default.DevToken;
                defaultConfig.LicenseLevelPrices = new LicenseLevelsPrice[3] {licenseLevel1, licenseLevel2, licenseLevel3};

                #region set up default payment settings

                var paymentSettings = new PaymentSettings();
                paymentSettings.PaymentURL = Settings.Default.PaymentURL;
                paymentSettings.PayPalAccountEmail = Settings.Default.PaymentAccountID;
                paymentSettings.PayPalCancelURL = "";
                paymentSettings.PayPalCurrencyCode = Settings.Default.PaymentCurrency;
                paymentSettings.PayPalIncludeNote = false;
                paymentSettings.PayPalItemNumber = Settings.Default.PaymentItemNumber;
                paymentSettings.PayPalLogoURL = "";
                paymentSettings.PayPalNoteTitle = "";
                paymentSettings.PayPalNotifyURL = Settings.Default.PaymentNotifyURL;
                paymentSettings.PayPalQuantity = 1;
                paymentSettings.PayPalRequireShippingAddress = false;
                paymentSettings.PayPalSuccessURL = "";
                paymentSettings.PayPalTag = "";
                defaultConfig.PaymentSettings = paymentSettings;

                #endregion

                defaultConfig.PollingTimerLength = Settings.Default.DefaultPollingInterval;
                defaultConfig.ProductName = Settings.Default.ProductName;
                defaultConfig.ServerPublicKey = "";
                defaultConfig.TrialLengthDays = Settings.Default.TrialLength;

                m_storage = defaultConfig;

                Logger.Log("DataStore : ReadDataFromIsolatedStorage : Initializing default datastore");
            }
        }

        private void ContactServerToRefreshStorage(DataStoreInfo dataStoreInfo)
        {
            Logger.Log("DataStore : ContactServerToRefreshStorage : Beginning");
            try
            {
                var sp = new Serverprocessing(dataStoreInfo);
                m_storage = sp.ContactServerForDefaultStorage(dataStoreInfo);
                Logger.Log("DataStore : ReadDataFromIsolatedStorage : Data Store Successfully Retrieved from server");
            }
            catch (Exception ex)
            {
                Logger.Log("DataStore : ReadDataFromIsolatedStorage : Error retrieving Data Store from server : " + ex.Message);
            }

            Logger.Log("DataStore : ContactServerToRefreshStorage : Completed");
        }


        private void read()
        {
            //load the stored data into the inner class so we have easy access to it.			
            IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForDomain();

            var dataProtector = new DataProtector(DataProtector.Store.USE_USER_STORE);

            try
            {
                isfs = new IsolatedStorageFileStream(DATA_STORE_NAME, FileMode.Open, FileAccess.Read, isf);
            }
            catch
            {
                //Error while reading in the storage file
                throw new DataStoreReadException("The inner exception was thrown while opening the islated storage file stream.");
            }

            //Read the file and put the data in our storage class.
            try
            {
                var byteBuffer = new byte[isfs.Length];

                while (isfs.Position < isfs.Length)
                {
                    byteBuffer[isfs.Position] = (byte) isfs.ReadByte();
                }


                byteBuffer = dataProtector.Decrypt(byteBuffer, null);

                var memStream = new MemoryStream(byteBuffer);

                var xtr = new XmlTextReader(memStream);
                var xs = new XmlSerializer(typeof (ClientAppConfig));
                m_storage = (ClientAppConfig) xs.Deserialize(xtr);
            }
            catch
            {
                //Error while reading in the storage file
                throw new DataStoreReadException("The inner exception was thrown while reading data from the isolated storage datastore.");
            }
            finally
            {
                isfs.Close();
            }
        }


        /// <summary>
        ///  Writes the storage object out to isolated storage
        ///  filename specified in: <see cref="DATA_STORE_NAME"/>
        /// </summary>
        /// <exception cref="DataStoreWriteException(string)"/>		
        protected void WriteDataToIsolatedStorage()
        {
            try
            {
                var memStream = new MemoryStream();

                var xs = new XmlSerializer(typeof (ClientAppConfig));
                xs.Serialize(memStream, m_storage);

                var dataProtector = new DataProtector(DataProtector.Store.USE_USER_STORE);
                var byteBuffer = new byte[memStream.Length];

                byteBuffer = memStream.GetBuffer();

                byteBuffer = dataProtector.Encrypt(byteBuffer, null);


                IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForDomain();
                var isfstream = new IsolatedStorageFileStream(DATA_STORE_NAME, FileMode.Create, FileAccess.Write, isf);

                isfstream.Write(byteBuffer, 0, byteBuffer.Length);

                isfstream.Close();
            }
            catch
            {
                throw new DataStoreWriteException("The inner exception was thrown while writing data to isolated storage.");
            }
        }

        /// <summary>
        /// This creates a unique machine identifier, namely the first mac 
        /// address found on the machine.
        /// </summary>		
        /// <returns>A string version of the mac address.</returns>
        /// <exception cref="DataStoreUniqueMachineIDException(string)"/>
        protected string CreateUniqueMachineID()
        {
            try
            {
                string cpuInfo = String.Empty;
                var mc = new ManagementClass("Win32_Processor");
                ManagementObjectCollection moc = mc.GetInstances();
                foreach (ManagementObject mo in moc)
                {
                    if (cpuInfo == String.Empty)
                    {
// only return cpuInfo from first CPU

                        cpuInfo = mo.Properties["ProcessorId"].Value.ToString();
                    }
                }
                return cpuInfo;
            }
            catch (Exception ex)
            {
                throw new DataStoreUniqueMachineIDException("Error while calculating the unique machine ID.", ex);
            }
        }

        #endregion

        #region Dispose of resources

        // Implement IDispose
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Free other state (managed object)
            }
            // Free your own state (unmanaged objects)


            // Set large field to null
        }

        // Use C# destructor syntax for finalization code
        ~DataStore()
        {
            Dispose(false);
        }

        #endregion

        /// <summary>
        /// The unique combination of licenseLevel, client identity, and product name that is transmitted
        /// to the server.  The returned license activation string is based on this value.
        /// </summary>
        /// <param name="licenseLevel">The requested license Level.</param>
        /// <param name="product">The name of the product.</param>
        /// <param name="clientIdentity">The unique identifier for the client.</param>
        /// <returns>A hashed combination of these values.</returns>
        public string ClientInfo(string licenseLevel, string product, string clientIdentity)
        {
            Logger.Log("DataStore : ClientInfo : Beginning");
            string clientInfo;
            var uniEncoding = new UnicodeEncoding();
            var sha = new SHA1CryptoServiceProvider();
            byte[] clientInfoByteArray;

            clientInfoByteArray = uniEncoding.GetBytes(licenseLevel + product + clientIdentity);


            clientInfo = Convert.ToBase64String(sha.ComputeHash(clientInfoByteArray));
            sha.Clear();


            Logger.Log("DataStore : ClientInfo : Complete");
            return clientInfo;
        }

        /// <summary>
        /// Sets the registrationKey received from the server.
        /// </summary>
        /// <param name="registrationKey">The registration key to set.</param>
        /// <param name="licenseLevel">The license level the key is good for.</param>
        /// <returns>True is the registration was verified and accepted, false otherwise.</returns>
        public Boolean SetRegistrationKey(string registrationKey, string licenseLevel)
        {
            Logger.Log("DataStore : SetRegistrationKey : Beginning");
            CCS.DataStore dataStoreSettings = m_storage.DataStoreSettings;

            m_storage.DataStoreSettings.LicenseLevel = licenseLevel;

            if (VerifyLicense(registrationKey, licenseLevel, dataStoreSettings.UniqueMachineID))
            {
                dataStoreSettings.RegistrationKey = registrationKey;
                dataStoreSettings.LicenseLevel = licenseLevel;
                m_storage.DataStoreSettings = dataStoreSettings;
                WriteDataToIsolatedStorage();
                Logger.Log("DataStore : SetRegistrationKey : Completed : True");
                return true;
            }
            else
            {
                Logger.Log("DataStore : SetRegistrationKey : Completed : False");
                return false;
            }
        }
    }

    /// <summary>
    /// Enumerated state for available product states {activated, Trial, Expiredtrial, Error}
    /// </summary>
    public enum ProductState
    {
        /// <summary>
        /// Enum state for a successfully activated product.
        /// </summary>
        Activated,

        /// <summary>
        /// Enum state for a product that is still in Trial mode (shareware).
        /// </summary>
        Trial,

        /// <summary>
        /// Enum state for a product that has not been activated, and the Trial
        /// has expired
        /// </summary>
        ExpiredTrial,

        /// <summary>
        /// If there is no datastore.  This occurs when the client is unable to reach the web service
        /// to initiailize the datastore.
        /// </summary>
        NoDataStore,

        /// <summary>
        /// Occurs when the product state is checked and the web service is still in progress.
        /// </summary>
        WebServiceInProgress,

        /// <summary>
        /// The product is in an unknown state.
        /// </summary>
        Error
    } ;

    /// <remarks>
    /// Summary description for DataStoreWriteException.
    /// </remarks>>
    [Serializable]
    public class DataStoreWriteException : Exception
    {
        public DataStoreWriteException()
        {
        }

        /// <summary>
        /// Thrown when the DataStore cannot write.
        /// </summary>
        /// <param name="message"></param>
        public DataStoreWriteException(string message) : base(message)
        {
        }

        /// <summary>
        /// Thrown because another exception was thrown
        /// during the datastore Write.
        /// </summary>
        /// <param name="message"></param>
        /// <param name="inner"></param>
        public DataStoreWriteException(string message, Exception inner) : base(message, inner)
        {
        }

        protected DataStoreWriteException(SerializationInfo si, StreamingContext c) : base(si, c)
        {
        }
    }

    /// <remarks>
    /// Summary description for DataStoreReadException.
    /// </remarks>
    [Serializable]
    public class DataStoreReadException : Exception
    {
        public DataStoreReadException()
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        public DataStoreReadException(string message) : base(message)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="inner"></param>
        public DataStoreReadException(string message, Exception inner) : base(message, inner)
        {
        }

        protected DataStoreReadException(SerializationInfo si, StreamingContext c) : base(si, c)
        {
        }
    }

    /// <remarks>
    /// Summary description for UniqueMachineIDException.
    /// </remarks>
    [Serializable]
    public class DataStoreUniqueMachineIDException : Exception
    {
        public DataStoreUniqueMachineIDException()
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        public DataStoreUniqueMachineIDException(string message) : base(message)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="inner"></param>
        public DataStoreUniqueMachineIDException(string message, Exception inner) : base(message, inner)
        {
        }

        protected DataStoreUniqueMachineIDException(SerializationInfo si, StreamingContext c) : base(si, c)
        {
        }
    }

    /// <remarks>
    /// Summary description for DataStoreChronologyException.
    /// </remarks>
    [Serializable]
    public class DataStoreChronologyException : Exception
    {
        public DataStoreChronologyException()
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        public DataStoreChronologyException(string message) : base(message)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="inner"></param>
        public DataStoreChronologyException(string message, Exception inner) : base(message, inner)
        {
        }

        protected DataStoreChronologyException(SerializationInfo si, StreamingContext c) : base(si, c)
        {
        }
    }

    /// <remarks>
    /// Summary description for DataStoreAppConfigException.
    /// </remarks>
    [Serializable]
    public class DataStoreAppConfigException : Exception
    {
        public DataStoreAppConfigException()
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        public DataStoreAppConfigException(string message) : base(message)
        {
        }

        protected DataStoreAppConfigException(SerializationInfo si, StreamingContext c) : base(si, c)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="inner"></param>
        public DataStoreAppConfigException(string message, Exception inner) : base(message, inner)
        {
        }
    }
}